package core

import (
	"errors"
	"fmt"
	"gitee.com/hongzhaomin/gspring/aware"
	"gitee.com/hongzhaomin/gspring/consts"
	"gitee.com/hongzhaomin/gspring/iface"
	"gitee.com/hongzhaomin/gspring/tools"
	"os"
	"reflect"
	"strings"
)

// ================================================================================
// ======================= CommonAnnotationBeanPostProcessor ======================
// ================================================================================

func NewCommonAnnotationBeanPostProcessor(beanFactory iface.BeanFactory) *CommonAnnotationBeanPostProcessor {
	return &CommonAnnotationBeanPostProcessor{
		beanFactory: beanFactory,
	}
}

var _ aware.BeanFactoryAware = (*CommonAnnotationBeanPostProcessor)(nil)
var _ iface.InstantiationAwareBeanPostProcessor = (*CommonAnnotationBeanPostProcessor)(nil)

// CommonAnnotationBeanPostProcessor 通用注解Bean增强器，该增强器实现了依赖注入功能
// 如果需要依赖注入的属性是普通字段，可以使用Tag[@Resource]来标记，Tag值表示需要注入的Bean的名称
// 如果需要依赖注入的属性是内嵌（继承）字段，可以使用Tag[@Extends]来标记，Tag值表示需要注入的Bean的名称
// 如果需要依赖注入的属性使用了Tag[@Value]来标记，Tag值为表达式[${server.port:8080}]表示需要注入的环境配置的值
type CommonAnnotationBeanPostProcessor struct {
	aware.Aware
	iface.AbstractInstantiationAwareBeanPostProcessor

	// bean工厂
	beanFactory iface.BeanFactory
}

func (my *CommonAnnotationBeanPostProcessor) SetBeanFactory(factory iface.BeanFactory) {
	my.beanFactory = factory
}

func (my *CommonAnnotationBeanPostProcessor) PostProcessProperties(beanName string, bean any) {
	inject := my.findResourceMetadata(bean)
	inject.Inject(beanName, my.beanFactory)
}

//func (my *CommonAnnotationBeanPostProcessor) PostProcessBeforeInitialization(beanName string, bean any) any {
//	return bean
//}

func (my *CommonAnnotationBeanPostProcessor) findResourceMetadata(bean any) *InjectionMetadata {
	rv := reflect.Indirect(reflect.ValueOf(bean))
	rt := rv.Type()
	elements := my.doFindResourceMetadata(rt, rv, bean)
	return NewInjectionMetadata(bean, elements)
}

func (my *CommonAnnotationBeanPostProcessor) doFindResourceMetadata(rt reflect.Type, rv reflect.Value, targetBean any) []InjectedElement {
	elements := make([]InjectedElement, 0)
	for i := 0; i < rv.NumField(); i++ {
		structField := rt.Field(i)
		rtField := structField.Type
		rvField := rv.Field(i)

		// 判断structField对象类型是否为指针
		rtFieldIsPtr := rtField.Kind() == reflect.Ptr
		if rtFieldIsPtr {
			rtField = rtField.Elem()
		}

		if beanName, resourceExist := structField.Tag.Lookup(consts.Resource); resourceExist {
			structFieldName := structField.Name
			if structField.Anonymous {
				panic(errors.New(fmt.Sprintf("@Resource不支持继承字段[%s]", structFieldName)))
			}
			if !structField.IsExported() {
				panic(errors.New(fmt.Sprintf("TAG被标注@Resource的字段[%s]必须为可导出的", structFieldName)))
			}
			if beanName == "" {
				beanName = tools.GetBeanName(structFieldName)
			}
			resourceElement := NewResourceElement(beanName, rtFieldIsPtr, rtField, rvField)
			elements = append(elements, resourceElement)
		} else if beanName, extendsExist := structField.Tag.Lookup(consts.Extends); extendsExist {
			anonymousFieldName := rtField.Name()
			if !structField.Anonymous {
				panic(errors.New(fmt.Sprintf("@Extends仅支持继承字段[%s]", anonymousFieldName)))
			}
			if !structField.IsExported() {
				panic(errors.New(fmt.Sprintf("TAG被标注@Extends的内嵌字段[%s]必须为可导出的", anonymousFieldName)))
			}
			if !((rtField.Kind() == reflect.Struct && rtFieldIsPtr) || rtField.Kind() == reflect.Interface) {
				msg := fmt.Sprintf("TAG被标注@Extends的内嵌字段[%s]类型必须是结构体指针、接口", anonymousFieldName)
				panic(errors.New(msg))
			}
			if beanName == "" {
				beanName = tools.GetBeanName(anonymousFieldName)
			}
			extendsElement := NewExtendsElement(beanName, rtFieldIsPtr, rtField, rvField)
			elements = append(elements, extendsElement)
		} else if expr, valueExist := structField.Tag.Lookup(consts.Value); valueExist {
			structFieldName := structField.Name
			if structField.Anonymous {
				panic(errors.New(fmt.Sprintf("@Value不支持继承字段[%s]", structFieldName)))
			}
			if !structField.IsExported() {
				panic(errors.New(fmt.Sprintf("TAG被标注@Value的字段[%s]必须为可导出的", structFieldName)))
			}
			if expr == "" {
				panic(errors.New(fmt.Sprintf("字段 [%s] 的TAG [@Value] 的值是空的", structFieldName)))
			}
			// 将 ${server.port:8080} 解析成 server.port:8080
			exprVal := os.Expand(expr, func(completeKey string) string {
				return completeKey
			})

			var key string
			var defaultVal string
			split := strings.Split(exprVal, ":")
			length := len(split)
			if length == 1 {
				// 没配默认值，exprVal就是key
				key = exprVal
			} else if length == 2 {
				key = split[0]
				defaultVal = split[1]
			} else {
				panic(fmt.Errorf("字段 [%s] 的TAG [@Value] 的值 [%s] 格式不正确", structFieldName, expr))
			}
			valueElement := NewValueElement(expr, key, defaultVal, rtFieldIsPtr, rtField, rvField)
			elements = append(elements, valueElement)
		} else if structField.Anonymous && structField.IsExported() {
			// 如果 structField 是可导出的继承字段（结构体或接口），且没有Tag标注@Extends，那么就不会注入该字段
			if rtField.Kind() == reflect.Struct {
				// 如果该内嵌结构体是非指针类型的结构体，则直接递归解析
				// 如果该内嵌结构体是指针类型的结构体，则直接创建一个新的结构体指针赋值上去，再递归解析
				if rtFieldIsPtr {
					rvField.Set(reflect.New(rtField))
					rvField = reflect.Indirect(rvField)
				}
				elements = append(elements, my.doFindResourceMetadata(rtField, rvField, targetBean)...)
			} else if rtField.Kind() == reflect.Interface {
				rtTargetBean := reflect.TypeOf(targetBean)
				if rtTargetBean.AssignableTo(rtField) {
					rvField.Set(reflect.ValueOf(targetBean))
				}
			}
		}
	}
	return elements
}
